Skip to content

fix: Reject the retries option with a custom transport - #642

Merged
razor-x merged 1 commit into
mainfrom
claude/python-sdk-audit-o2iid9-09-transport-retries
Aug 28, 2026
Merged

fix: Reject the retries option with a custom transport#642
razor-x merged 1 commit into
mainfrom
claude/python-sdk-audit-o2iid9-09-transport-retries

Conversation

@razor-x

@razor-x razor-x commented Aug 27, 2026

Copy link
Copy Markdown
Member

What

A caller-supplied transport silently disabled retries: Seam(retries=Retry(total=5), httpx_options={"transport": MyTransport()}) performed zero retries with no warning, while env-proxy transports kept them — so retry behavior depended on whether HTTPS_PROXY happened to be set (SDK audit finding M12). Caller-supplied mounts had the same hole.

Following the PHP wave's "an injected client is left untouched" decision (and the injected-client guard the audit praised in the Ruby SDK), the SDK now:

  • Raises SeamInvalidOptionsError when retries= is explicitly passed together with a custom transport or mounts — the caller who brings a transport owns its middleware, and mixing an explicit retry policy with a transport that bypasses it is always a silent lie. Both sync and async clients.
  • Leaves custom transports/mounts untouched and un-retried when retries is not passed (status quo, now documented).
  • Documents the opt-in recipe in the README's retries section: wrap your transport with httpx_retries.RetryTransport.

The client's retries parameter default changes from DEFAULT_RETRIES to None — behaviorally identical (the constructor already coalesced None to the default), but it makes "explicitly passed" well-defined end-to-end. SDK-built transports, including env-proxy mounts, keep their default retry wrapping.

Testing

New test/custom_transport_test.py: the conflict raises with a pinned message (transport and mounts, sync and async); a custom transport without retries makes exactly 1 request against a 503-then-200 server; the documented RetryTransport recipe retries (request count 2, response succeeds).

Revert check: with seam/client.py reverted to main, the conflict test fails with DID NOT RAISE — the audit's silent-acceptance symptom.

Full suite: 190 passed; mypy, pylint (10.00), black, rstcheck clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Y1RzepycXEYA3LStfjt8cY


Generated by Claude Code

@razor-x
razor-x requested a review from a team as a code owner August 27, 2026 21:45
@razor-x
razor-x force-pushed the claude/python-sdk-audit-o2iid9-09-transport-retries branch from 323893d to e04cba4 Compare August 28, 2026 19:30
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y1RzepycXEYA3LStfjt8cY
@razor-x
razor-x force-pushed the claude/python-sdk-audit-o2iid9-09-transport-retries branch from e04cba4 to 2e290a4 Compare August 28, 2026 20:43
@razor-x
razor-x merged commit c1e3b6a into main Aug 28, 2026
23 checks passed
@razor-x
razor-x deleted the claude/python-sdk-audit-o2iid9-09-transport-retries branch August 28, 2026 21:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants